package com.qen.wrapper.resultmap;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import com.qen.common.ClassDescHelper;
import com.qen.common.ColumnCache;
import com.qen.common.FieldCache;
import com.qen.common.LambdaUtils;
import com.qen.common.TableInfoHelper;
import com.qen.interfaces.MFunction;
import com.qen.interfaces.SFunction;
import com.qen.segments.SelectCache;
import lombok.Getter;
import org.beetl.sql.clazz.TableDesc;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;

/**
 * 无泛型约束 实现自由映射
 *
 * @author licz
 * @since 1.0.0
 */
@Getter
public class BeetlLabelFree<T> implements Label<T> {

    private String property;

    private Class<?> javaType;

    private Class<T> ofType;

    private List<IResult> resultList;

    /**
     * wrapper里面的引用
     */
    private List<Label<?>> beetlLabels;

    private BeetlLabelFree() {
    }

    @SuppressWarnings({"unused", "unchecked", "DuplicatedCode"})
    public static class Builder<T> {

        private final BeetlLabelFree<T> beetlLabel;

        /**
         * 手动构建
         *
         * @param property property
         * @param javaType javaType
         * @param ofType   映射类
         */
        public Builder(String property, Class<?> javaType, Class<T> ofType) {
            this.beetlLabel = new BeetlLabelFree<>();
            beetlLabel.property = property;
            beetlLabel.javaType = javaType;
            beetlLabel.ofType = ofType;
            beetlLabel.resultList = new BeetlResultList();
            beetlLabel.beetlLabels = new ArrayList<>();
        }

        public <E> Builder<T> all(Class<E> entityClass) {
            allBuild(null, entityClass);
            return this;
        }

        public <E> Builder<T> all(String prefix, Class<E> entityClass) {
            allBuild(prefix, entityClass);
            return this;
        }

        /**
         * 映射实体字段过滤(含主键)
         */
        public <E> Builder<T> filter(Class<E> entityClass, Predicate<SelectCache> predicate) {
            Map<String, FieldCache> fieldMap = ClassDescHelper.getFieldMap(beetlLabel.ofType);
            ColumnCache.getListField(entityClass).stream().filter(predicate)
                    .filter(p -> fieldMap.containsKey(p.getColumProperty())).forEach(c ->
                            beetlLabel.resultList.add(new BeetlResult.Builder<T>(false, null, c).build()));
            return this;
        }

        /**
         * 映射实体字段过滤(含主键)
         */
        public <E> Builder<T> filter(String prefix, Class<E> entityClass, Predicate<SelectCache> predicate) {
            Map<String, FieldCache> fieldMap = ClassDescHelper.getFieldMap(beetlLabel.ofType);
            ColumnCache.getListField(entityClass).stream().filter(predicate)
                    .filter(p -> fieldMap.containsKey(p.getColumProperty())).forEach(c ->
                            beetlLabel.resultList.add(new BeetlResult.Builder<T>(false, prefix, c).build()));
            return this;
        }

        public <E> Builder<T> id(SFunction<E, ?> entity, SFunction<T, ?> tag) {
            BeetlResult.Builder<T> builder = new BeetlResult.Builder<>(true, null);
            builder.column(entity).property(tag);
            beetlLabel.resultList.add(builder.build());
            return this;
        }

        public <E> Builder<T> id(SFunction<E, ?> entity) {
            BeetlResult.Builder<T> builder = new BeetlResult.Builder<>(true, null);
            builder.column(entity);
            beetlLabel.resultList.add(builder.build());
            return this;
        }

        public <E> Builder<T> id(String index, SFunction<E, ?> entity, SFunction<T, ?> tag) {
            BeetlResult.Builder<T> builder = new BeetlResult.Builder<>(true, index);
            builder.column(entity).property(tag);
            beetlLabel.resultList.add(builder.build());
            return this;
        }

        public <E> Builder<T> id(String index, SFunction<E, ?> entity) {
            BeetlResult.Builder<T> builder = new BeetlResult.Builder<>(true, index);
            builder.column(entity);
            beetlLabel.resultList.add(builder.build());
            return this;
        }

        public <E> Builder<T> result(SFunction<E, ?> entity, SFunction<T, ?> tag) {
            BeetlResult.Builder<T> builder = new BeetlResult.Builder<>(false, null);
            builder.column(entity).property(tag);
            beetlLabel.resultList.add(builder.build());
            return this;
        }

        public <E> Builder<T> result(SFunction<E, ?> entity) {
            BeetlResult.Builder<T> builder = new BeetlResult.Builder<>(false, null);
            builder.column(entity);
            beetlLabel.resultList.add(builder.build());
            return this;
        }

        public <E> Builder<T> result(String index, SFunction<E, ?> entity, SFunction<T, ?> tag) {
            BeetlResult.Builder<T> builder = new BeetlResult.Builder<>(false, index);
            builder.column(entity).property(tag);
            beetlLabel.resultList.add(builder.build());
            return this;
        }

        public <E> Builder<T> result(String index, SFunction<E, ?> entity) {
            BeetlResult.Builder<T> builder = new BeetlResult.Builder<>(false, index);
            builder.column(entity);
            beetlLabel.resultList.add(builder.build());
            return this;
        }

        public <A, R, B extends Collection<R>> Builder<T> collection(Class<A> entityClass, SFunction<T, B> func) {
            return collection(null, entityClass, func);
        }

        /**
         * 嵌套
         */
        public <A, R, B extends Collection<R>> Builder<T> collection(String prefix, Class<A> entityClass, SFunction<T, B> func) {
            String dtoFieldName = LambdaUtils.getName(func);
            Class<T> dtoClass = LambdaUtils.getEntityClass(func);
            Map<String, FieldCache> fieldMap = ClassDescHelper.getFieldMap(dtoClass);
            FieldCache field = fieldMap.get(dtoFieldName);
            Class<?> genericType = ClassDescHelper.getGenericType(field.getField());
            BeetlLabel.Builder<A, R> builder;
            if (genericType == null || genericType.isAssignableFrom(entityClass)) {
                //找不到集合泛型 List List<?> List<Object> ， 直接查询数据库实体
                builder = new BeetlLabel.Builder<>(prefix, dtoFieldName, entityClass, field.getType());
            } else {
                Class<R> ofType = (Class<R>) genericType;
                builder = new BeetlLabel.Builder<>(prefix, dtoFieldName, entityClass, field.getType(), ofType, true);
            }
            beetlLabel.beetlLabels.add(builder.build());
            return this;
        }

        public <A, R, B extends Collection<R>> Builder<T> collection(Class<A> entityClass, SFunction<T, B> func, MFunction<BeetlLabel.Builder<A, R>> mFunc) {
            return collection(null, entityClass, func, mFunc);
        }

        public <A, R, B extends Collection<R>> Builder<T> collection(SFunction<T, B> func,
                                                                     MFunction<Builder<R>> mFunc) {
            String dtoFieldName = LambdaUtils.getName(func);
            Class<T> dtoClass = LambdaUtils.getEntityClass(func);
            FieldCache field = ClassDescHelper.getFieldMap(dtoClass).get(dtoFieldName);
            //获取集合泛型
            Class<?> genericType = ClassDescHelper.getGenericType(field.getField());
            Class<R> ofType = (Class<R>) genericType;
            Builder<R> builder = new Builder<>(dtoFieldName, field.getType(), ofType);
            beetlLabel.beetlLabels.add(mFunc.apply(builder).build());
            return this;
        }

        /**
         * 嵌套
         */
        public <A, R, B extends Collection<R>> Builder<T> collection(String prefix,
                                                                     Class<A> entityClass,
                                                                     SFunction<T, B> func,
                                                                     MFunction<BeetlLabel.Builder<A, R>> mFunc) {
            String dtoFieldName = LambdaUtils.getName(func);
            Class<T> dtoClass = LambdaUtils.getEntityClass(func);
            FieldCache field = ClassDescHelper.getFieldMap(dtoClass).get(dtoFieldName);
            //获取集合泛型
            Class<?> genericType = ClassDescHelper.getGenericType(field.getField());
            Class<R> ofType = (Class<R>) genericType;
            BeetlLabel.Builder<A, R> builder = new BeetlLabel.Builder<>(prefix, dtoFieldName, entityClass, field.getType(), ofType, false);
            beetlLabel.beetlLabels.add(mFunc.apply(builder).build());
            return this;
        }

        public <A, B> Builder<T> association(Class<A> child, SFunction<T, B> dtoField) {
            return association(null, child, dtoField);
        }

        /**
         * 嵌套
         */
        public <A, B> Builder<T> association(String index, Class<A> child, SFunction<T, B> dtoField) {
            Class<T> dtoClass = LambdaUtils.getEntityClass(dtoField);
            Map<String, FieldCache> fieldMap = ClassDescHelper.getFieldMap(dtoClass);
            String dtoFieldName = LambdaUtils.getName(dtoField);
            FieldCache field = fieldMap.get(dtoFieldName);
            Assert.isFalse(Collection.class.isAssignableFrom(field.getType()), "association 不支持集合类");
            BeetlLabel.Builder<A, B> builder;
            builder = new BeetlLabel.Builder<>(index, dtoFieldName, child, field.getType(), (Class<B>) field.getType(), true);
            beetlLabel.beetlLabels.add(builder.build());
            return this;
        }

        public <A, B> Builder<T> association(Class<A> child, SFunction<T, B> dtoField,
                                             MFunction<BeetlLabel.Builder<A, B>> collection) {
            return association(null, child, dtoField, collection);
        }

        /**
         * 嵌套
         */
        public <A, B> Builder<T> association(String index, Class<A> child, SFunction<T, B> dtoField,
                                             MFunction<BeetlLabel.Builder<A, B>> collection) {
            String dtoFieldName = LambdaUtils.getName(dtoField);
            Class<T> dtoClass = LambdaUtils.getEntityClass(dtoField);
            FieldCache field = ClassDescHelper.getFieldMap(dtoClass).get(dtoFieldName);
            Assert.isFalse(Collection.class.isAssignableFrom(field.getType()), "association 不支持集合类");
            BeetlLabel.Builder<A, B> builder = new BeetlLabel.Builder<>(index, dtoFieldName, child, field.getType(), (Class<B>) field.getType(), false);
            beetlLabel.beetlLabels.add(collection.apply(builder).build());
            return this;
        }

        public BeetlLabelFree<T> build() {
            if (CollectionUtil.isEmpty(beetlLabel.resultList)) {
                TableDesc tableInfo = TableInfoHelper.getTableInfo(beetlLabel.ofType);
                Assert.notNull(tableInfo,
                        "无法自动映射, 找不到 <%s> 对应的表, 请使用 .all(xxx.class), id()或者result() 手动映射",
                        beetlLabel.ofType.getSimpleName());
                all(beetlLabel.ofType);
            }
            return beetlLabel;
        }

        private void allBuild(String prefix, Class<?> entityClass) {
            Map<String, FieldCache> tagMap = ClassDescHelper.getFieldMap(beetlLabel.getOfType());
            List<SelectCache> listField = ColumnCache.getListField(entityClass);
            for (SelectCache s : listField) {
                FieldCache field = tagMap.get(s.getColumProperty());
                if (Objects.nonNull(field)) {
                    BeetlResult result = new BeetlResult();
                    result.setIndex(prefix);
                    result.setId(s.isPk());
                    result.setJavaType(field.getType());
                    result.setProperty(s.getColumProperty());
                    result.setSelectNormal(s);
                    beetlLabel.resultList.add(result);
                }
            }
        }
    }
}
